﻿using System.Collections.Generic;
using System.Linq;
using StockAccessors;
using StockEngine.Extensions;
using StockModel;
using System;
using Utils;
using Utils.Extensions;

namespace StockEngine.RealTime
{
	internal class GroupInitializer : GroupInitializerBase
	{
		#region Attributes

		private readonly IStockDataAccessor _accessor;

		#endregion

		#region Public Methods

		public GroupInitializer(IStockDataAccessor accessor, StockGroupDataSource stockGroupDataSource, QuotationDataSource quotationDataSource, bool enableParallel)
			: base(stockGroupDataSource, quotationDataSource, enableParallel)
		{
			_accessor = accessor;
		}

		public void ExecuteFirstTimeInitialization()
		{
			foreach (EngineStockGroup @group in this.Groups)
			{
				base.GroupDataSource.LoadMarket(group);

				DateTime dayToLoad = GroupInitializer.FindDayToLoad(group);

				base.GroupDataSource.Load(group, dayToLoad);

				try
				{
					foreach (StockGroupItem stock in group.Items.GetReadLock())
					{
						base.LoadDailyQuotationsToStock(group, stock.Item, dayToLoad);
					}
				}
				finally
				{
					group.Items.FreeReadLock();
				}
			}
		}

		public bool CanOpenGroup(EngineStockGroup group)
		{
			DateTime now = DateTime.Now;

			AccessorGroup accessorGroup = _accessor.Groups.FirstOrDefault(g => g != null && g.Name == group.Name);

			// Accessor group can be null if the accessor does not contain this group
			bool result;
			if (accessorGroup != null)
			{
				result = accessorGroup.Stocks.All(s => s.Acquisition.Day == now.Day) && group.Market.IsOpen(now);
			}
			else
			{
				result = false;
			}

			return result;
		}

		public void OpenGroup(EngineStockGroup group)
		{
			DateTime now = DateTime.Now;
			AccessorGroup accessorGroup = _accessor.Groups.FirstOrDefault(g => g != null && g.Name == group.Name);

			// Accessor group can be null if the accessor does not contain this group
			if (accessorGroup != null)
			{
				bool alreadyInitialized;
				try
				{
					ObservableCollection2<StockGroupItem> stocks = group.Items.GetReadLock();
					alreadyInitialized = stocks.Count > 0 && stocks.All(g => g.Item.QuotationCalendar.IsDayCreated(now));
				}
				finally
				{
					group.Items.FreeReadLock();
				}

				if (!alreadyInitialized)
				{
					group.ClearStocks();

					List<StockGroupItem> items = new List<StockGroupItem>();
					foreach (AccessorStockQuote accessorStockQuote in accessorGroup.Stocks)
					{
						Stock stock = new Stock(accessorStockQuote.Symbol, accessorStockQuote.Name);
						StockGroupItem groupItem = new StockGroupItem(stock);

						stock.QuotationCalendar.Clear();
						stock.QuotationCalendar.CreateDay(now);
						items.Add(groupItem);
					}

					ObservableCollection2<StockGroupItem> groupItems = group.Items.GetWriteLock();
					try
					{
						groupItems.AddRange(items);

						foreach (StockGroupItem stockGroupItem in groupItems) // This has to be done after adding the stocks to the group to catch the changed event.
						{
							base.LoadPreviousClosingValue(group, stockGroupItem.Item, now);
						}
					}
					finally
					{
						group.Items.FreeWriteLock();
					}

					base.GroupDataSource.Save(group);
				}
				group.IsInitialized = true;
			}
		}

		#endregion

		#region Private Methods

		private static DateTime FindDayToLoad(StockGroup stockGroup)
		{
			DateTime result = DateTime.Now; // Load today quotations by default

			if (!result.IsWeekend() && !stockGroup.Market.Holidays.Contains(result))
			{
				if (result.TimeOfDay < stockGroup.Market.PreOpen)
				{
					// Load previous working day quotations
					result = stockGroup.Market.FindPreviousWorkingDay(result);
				}
			}
			else
			{
				// Load previous working day quotations
				result = stockGroup.Market.FindPreviousWorkingDay(result);
			}

			return result.Date;
		}

		#endregion

	}
}
